
//--Yangxin--

`include "defines.v"

module wb_stage(
	input  wire 						clk    		     ,
	input  wire 						reset		     ,
	//allowin
	output wire 						ws_allowin	     ,
	//from ms
	input  wire                         ms_to_ws_valid   ,
	input  wire [`MS_TO_WS_BUS_WD-1 :0] ms_to_ws_bus     ,
	//to rf:for write back
	output wire [`WS_TO_RF_BUS_WD -1:0] ws_to_rf_bus     ,
	//to csr
	output wire [`WS_TO_CSR_BUS_WD-1:0] ws_to_csr_bus    , 
	output wire [`WS_EXCEPT_BUS_WD-1:0] ws_except_bus    , 
	//forward
	output wire [                 68:0] ws_to_ds_fwd     ,
	output wire [                128:0] csr_ws_to_es_fwd , 
	//csr_regs_forward
	input  wire [                 63:0] csr_mtvec        , 
	input  wire [                 63:0] csr_mepc         ,
	input  wire [                 63:0] csr_mstatus      ,	
	input  wire 				        csr_mie_mtie     , 		
    //trace debug inteface: I think it is similar to difftest
	output wire [                 63:0] debug_wb_pc      ,
	output wire [                 31:0] debug_wb_inst    , 
	output wire 						debug_wb_rf_we   ,
	output wire [                  4:0] debug_wb_rf_waddr,
	output wire [                 63:0] debug_wb_rf_wdata, 
	output wire                         debug_wb_valid   ,
	//except
	output wire                         except_flush     ,   
    output wire [                 63:0] except_new_pc    ,
	//timer interrupt 
	input  wire                         timer_interrupt  ,
	output wire                         true_timer_interrupt, 
	input  wire	[                 63:0] ms_pc_to_ws      ,
	input  wire                         ms_valid_to_ws   ,
	input  wire [                 63:0] es_pc_to_ws      ,
	input  wire                         es_valid_to_ws   ,
	output wire                         ws_skip          ,
	//inst_sram data ok
	input  wire 				        data_ok 
	);




reg ws_valid;
wire ws_ready_go;

reg [`MS_TO_WS_BUS_WD -1:0] ms_to_ws_bus_r;
wire ws_gr_we;
wire [4:0] ws_dest;
wire [63:0] ws_final_result;
wire [63:0] ws_pc;
wire [31:0] ws_inst;
//csr
wire 		 ws_csr_wr;
wire [63:0]  ws_csr_wdata;
wire [11:0]  ws_csr_waddr;
//exception
wire         ws_except_enter_ecall;
wire         ws_except_quit_mret  ;
// wire         except_flush         ;
// wire [63:0]  except_new_pc        ;





wire        ws_except_ecall		 ;
wire        ws_except_mret 		 ;
wire [63:0] ws_except_pc         ; 
wire        ws_except_status_mie ;
wire 	    ws_except_status_mpie; 

wire        ws_timer_interrupt   ;       
wire [63:0] ws_int_next_pc       ;

// assign ws_int_next_pc = ms_valid_to_ws ? ms_pc_to_ws :
// 						es_valid_to_ws ? es_pc_to_ws :
// 								         64'h0       ;
assign ws_int_next_pc = ws_except_pc;


assign ws_except_ecall       = ws_except_enter_ecall & ws_valid;
assign ws_except_mret        = ws_except_quit_mret & ws_valid;
assign ws_except_pc          = ws_pc;
assign ws_except_status_mie  = csr_mstatus[3];
assign ws_except_status_mpie = csr_mstatus[7];

assign ws_timer_interrupt    = timer_interrupt & ws_except_status_mie & csr_mie_mtie & ws_valid;         //need to add interrupt-en ???
//assign ws_timer_interrupt    = timer_interrupt & ws_except_status_mie & csr_mie_mtie;         //need to add interrupt-en ???
assign true_timer_interrupt  = ws_timer_interrupt;

wire except_flush_temp;

assign except_flush_temp  = (ws_except_enter_ecall | ws_except_quit_mret | ws_timer_interrupt) & ws_valid;

reg except_flush_hold;
always @(posedge clk) begin
	if(reset) begin
		except_flush_hold <= 1'b0;
	end
	else if(except_flush_temp) begin
		except_flush_hold <= 1'b1;
	end
	else if(data_ok) begin
		except_flush_hold <= 1'b0;
	end
end
assign except_flush = except_flush_temp | except_flush_hold;

//assign except_flush  = (ws_except_enter_ecall | ws_except_quit_mret | ws_timer_interrupt);
assign except_new_pc = ws_timer_interrupt    ? csr_mtvec :
					   ws_except_enter_ecall ? csr_mtvec :
					   ws_except_quit_mret   ? csr_mepc  :
					   						   csr_mtvec ;

assign ws_except_bus         = {ws_int_next_pc       ,  //132:69
								ws_timer_interrupt   ,  //68:68
								ws_except_ecall      ,  //67:67
							    ws_except_mret       ,  //66:66
								ws_except_pc         ,  //65:2
								ws_except_status_mie ,  //1:1
								ws_except_status_mpie   //0:0
};




//////////////
wire [4:0] WB_dest;
assign WB_dest = ws_dest & {5{ws_valid}};

//wire ws_skip;

//forward
//wire [68:0] ws_to_ds_fwd;
assign ws_to_ds_fwd     = {69{ws_valid}} & {69{ws_gr_we}} & {ws_dest,ws_final_result};  //5+64
assign csr_ws_to_es_fwd = {129{ws_valid}} & {129{ws_csr_wr}} & 
						  {ws_csr_wr,ws_csr_waddr,ws_csr_wdata}; //1+64+64

assign {ws_skip              ,
		ws_except_enter_ecall,
		ws_except_quit_mret ,
		ws_csr_wr      ,
		ws_csr_wdata   ,
		ws_csr_waddr   ,
		ws_inst        ,     //166:135
		ws_gr_we       ,     //134:133
	    ws_dest        ,     //132:128
	    ws_final_result,     //127:64
	    ws_pc                //63:0
	    } = ms_to_ws_bus_r;


wire        rf_we    ;
wire [ 4:0] rf_waddr ;
wire [63:0] rf_wdata ;

//csr
wire        csr_we    ;
wire [63:0] csr_waddr ;
wire [63:0] csr_wdata ;
assign ws_to_rf_bus = {rf_we,       //69:69
	                   rf_waddr,    //68:64
	                   rf_wdata     //63:0
					   };

assign ws_to_csr_bus = {csr_we,      //128:128
						csr_waddr,   //127:64
						csr_wdata    //63:0
					   };


assign ws_ready_go = 1'b1;
assign ws_allowin = !ws_valid || ws_ready_go;
always @(posedge clk) begin
	if (reset | except_flush) begin
		// reset
		ws_valid <= 1'b0;
	end
	else if (ws_allowin) begin
		ws_valid <= ms_to_ws_valid;
	end

	if(ms_to_ws_valid && ws_allowin) begin
		ms_to_ws_bus_r <= ms_to_ws_bus;
	end
end

assign rf_we    = ws_gr_we && ws_valid && (~true_timer_interrupt);
//assign rf_we    = ws_gr_we && ws_valid;
assign rf_waddr = ws_dest;
assign rf_wdata = ws_final_result;
//csr
//assign csr_we    = ws_csr_wr && ws_valid;
assign csr_we    = ws_csr_wr && ws_valid && (~true_timer_interrupt);
assign csr_waddr = {52'h0,ws_csr_waddr};
assign csr_wdata = ws_csr_wdata;
//csr with exception
//assign csr_we    = ws_except_enter_ecall ?  (ws_csr_wr && ws_valid);


reg  [                 63:0] ws_pc_d1;
reg  [                 31:0] ws_inst_d1;
reg  						 rf_we_d1;
reg  [                  4:0] ws_dest_d1;
reg  [                 63:0] ws_final_result_d1;

always @(posedge clk) begin
	ws_pc_d1 <= ws_pc;
	ws_inst_d1 <= ws_inst;
	rf_we_d1 <= rf_we;
	ws_dest_d1 <= ws_dest;
	ws_final_result_d1 <= ws_final_result;
end


//debug info generate
assign debug_wb_pc       = ws_pc          ;
assign debug_wb_inst     = ws_inst        ;
assign debug_wb_rf_we    = rf_we          ;
assign debug_wb_rf_waddr = ws_dest        ;
assign debug_wb_rf_wdata = ws_final_result;
assign debug_wb_valid    = ws_valid       ;
// assign debug_wb_pc       = ws_pc          ;
// assign debug_wb_inst     = ws_inst_d1        ;
// assign debug_wb_rf_we    = rf_we_d1          ;
// assign debug_wb_rf_waddr = ws_dest_d1        ;
// assign debug_wb_rf_wdata = ws_final_result_d1;

endmodule
